<?php
/**
 * Created by PhpStorm.
 * User: dev
 * Date: 8/10/14
 * Time: 3:49 PM
 */
/*

--
-- Table structure for table `hierarchy`
--

CREATE TABLE `hierarchy` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(55) NOT NULL,
  `parent_id` int(11) DEFAULT NULL,
  `lineage` text,
  `deep` int(3) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;


*/

class Hierarchy {

    public function __construct() {
        parent::__construct();
    }

    // Fetch all records based on the primary key, ordered by their lineage.
    // param - integer - allows you to return only from a certain point  (optional)
    // Returns result_array
    public static function get($table,$top_id=0)
    {
        if (!empty($top_id))
        {
            $parent = Hierarchy::get_one($table,$top_id);

            if (!empty($parent))
            {
                $query = DB::table($table)->where('lineage', 'LIKE', $parent->lineage.'%')
                    ->order_by('lineage', 'asc')->get();
            }

        }
        return $query;
    }

    // Fetch a single record based on the primary key.
    // Returns row_array
    public static function get_one($table,$id)
    {
        $row = DB::table($table)->where('id','=', $id)->first();
        return $row;
    }

    // Fetch all direct child records based on the parent id, ordered by their lineage.
    // param - integer - parent id of child records
    // Returns result_array
    public static  function get_children($table,$parent_id, $limit = 99999)
    {
        //$query = $this->db->order_by($this->lineage)->where($this->parent_id, $parent_id)->get($this->table);
        $query = DB::table($table)->where('parent_id','=', $parent_id)->take($limit)->order_by('lineage')->get();
        return $query;
    }

    // Fetch all descendent records based on the parent id, ordered by their lineage.
    // param - integer - parent id of descendent records
    // Returns result_array
    public static function get_descendents($table,$parent_id)
    {
        $parent = Hierarchy::get_one($table,$parent_id);
        if (empty($parent)) return array();

        // note that adding '-' to the like leaves out the parent record
        //$query = $this->db->order_by($this->lineage)->like($this->lineage, $parent[$this->lineage].'-', 'after')->get($this->table);
        $query = DB::table($table)->where('lineage', 'LIKE', $parent->lineage.'-'.'%')
            ->order_by('lineage')->get();
        return $query;
    }

    // Fetch all ancestor records based on the id, ordered by their lineage (top to bottom).
    // param - integer - id of descendent record
    // Returns result_array
    public static  function get_ancestors($table, $id, $remove_this = false)
    {
        $current = Hierarchy::get_one($table,$id);
        if (empty($current)) return array();
        $lineage_ids = explode('-' , $current->lineage);
        if ($remove_this) unset($lineage_ids[count($lineage_ids)-1]);

        $query = DB::table($table)->where_in('id',$lineage_ids)->order_by('lineage','ASC')->get();
        return $query;
    }

    // Fetch parent of record based on the id
    // param - integer - id of descendent record
    // Returns row
    public static  function get_parent($table,$id)
    {
        $current = Hierarchy::get_one($table,$id);
        if (empty($current)) return array();

        $query = DB::table($table)->where('id','=', $current->parent_id)->first();
        return $query;
    }

    // Fetch all descendent records based on the parent id, ordered by their lineage, and groups them as a mulit-dimensional array.
    // param - integer - parent id of descendent records (optional)
    // Returns result_array
    public static  function get_grouped_children($table,$top_id=0)
    {

        $result = Hierarchy::get($table,$top_id);
        $grouped_result = Hierarchy::_findChildren($result);

        return $grouped_result;
    }

    // inserts new record. If no parent_id included, assumes top level item
    // returns result of final statement
    public static  function insert($table,$data)
    {

        if(!empty($data['parent_id']))
        {
            //get parent info
            $parent = Hierarchy::get_one($table,$data['parent_id']);
            $data['deep'] = $parent->deep + 1;
        }

        $insert_id = DB::table($table)->insert_get_id($data);

        $lin = (empty($parent->lineage))? str_pad($insert_id, 5 ,'0', STR_PAD_LEFT): $parent->lineage.'-'.str_pad($insert_id, 5, '0', STR_PAD_LEFT);
        return DB::table($table)
            ->where('id', '=', $insert_id)
            ->update(array(
                'lineage'
                => $lin
            ));
    }

    // deletes record
    // param - true/false - delete all descendent records
    public static  function delete($table, $id, $with_children=true)
    {
        //little clumsy, due to some Active Record restrictions
        if ($with_children)
        {
            $parent = Hierarchy::get_one($table,$id);
        }

        if (!empty($parent) && $with_children)
        {
            DB::table($table)->where('id', '=', $id)
                ->or_where('lineage', 'LIKE', $parent->lineage.'-'.'%')
                ->delete();
        }

    }

    static function resync($table)
    {
        //we could probably just re-write this with two copies of your table, and update. I think this will run safer and leave less to worry
        //$current_data = $this->db->select($this->primary_key. ', ' . $this->parent_id)->order_by($this->parent_id, 'asc')->get($this->table)->result_array();
        $current_data = DB::table($table)->select(array('id','parent_id'))->order_by('parent_id')->get();
        //dd($current_data);
        if (!empty($current_data))
        {
            foreach ($current_data as $row) {

                $update['deep'] = 0;

                if(!empty($row->parent_id))
                {
                    //get parent info
                    $parent = Hierarchy::get_one($table,$row->parent_id);
                    $update['deep'] = $parent->deep + 1;
                }
                $update['lineage'] = (empty($parent->lineage))? str_pad($row->id, 5 ,'0', STR_PAD_LEFT): $parent->lineage.'-'.str_pad($row->id, 5, '0', STR_PAD_LEFT);
                DB::table($table)
                    ->where('id', '=', $row->id)
                    ->update($update);
                unset($parent);
            }
        }
    }

    // gets the maximum depth of any branch of tree
    // returns integer
    public static function max_deep($table, $id)
    {
        $parent = Hierarchy::get_one($table,$id);
        if (empty($parent)) return 0;

        // note that adding '-' to the like leaves out the parent record
        $query = DB::table($table)->where('lineage', 'LIKE', $parent->lineage.'-'.'%')
                ->max('deep');

        $row = $query;
        if($row){
            return $row;
        }else{
            return 0;
        }

        //$row = $this->db->select_max($this->deep, 'max_deep')->get($this->table)->row_array();
        //return $row['max_deep'] + 1; //deep starts at 0
    }

















    // Thank you, http://stackoverflow.com/users/427328/elusive
    static function _findChildren(&$nodeList, $parentId = null) {
        $nodes = array();

        foreach ($nodeList as $node) {

            //dd($node->parent_id);

            if ($node->parent_id == $parentId) {
                $node['children'] = Hierarchy::_findChildren($nodeList, $node->id);
                $nodes[] = $node;
            }
        }

        return $nodes;
    }

} 